package demo.service;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.dingtalk.api.DefaultDingTalkClient;
import com.dingtalk.api.DingTalkClient;
import com.dingtalk.api.request.*;
import com.dingtalk.api.response.*;

import com.taobao.api.ApiException;
import demo.Main;
import demo.entity.tools.ServiceCodeEnum;
import demo.entity.tools.ServiceException;
import demo.entity.vo.ActualAttendanceVo;
import demo.entity.vo.YearActualAttendanceVO;
import demo.utils.LocalDateUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicIntegerArray;
import java.util.stream.Collectors;

/**
 * @Author Zhengx
 * @Date 2019/9/23
 * @Description 调用前先确认当前月有无工作日处于周六，若有则设置
 */
public class OutputService {

    private final Logger log = LoggerFactory.getLogger(this.getClass());

    public OutputService() {
    }

    private String accessTokenUrl = "https://oapi.dingtalk.com/gettoken";

    private String appKey = "ding9okx3wgcikxrdwtg";

    private String appSecret = "hX9Tb0S-XJnrgUxu6Wv1NG9HKXRKjR8YF1NIsZWaYURLK_6kiguCe9s6UG-Y7LMV";

    private Integer year = 2021;

    private static final String FIELD = "sys00-name";
    private static final String ON_DUTY = "OnDuty";
    private static final BigDecimal STANDARD_DAYS = new BigDecimal(22);
    private static final String RECKONER = "施丽雯";

    public String getAccessToken() throws ApiException {
        DefaultDingTalkClient client = new DefaultDingTalkClient(accessTokenUrl);
        OapiGettokenRequest request = new OapiGettokenRequest();
        request.setAppkey(appKey);
        request.setAppsecret(appSecret);
        request.setHttpMethod("GET");
        OapiGettokenResponse response = client.execute(request);
        return response.getAccessToken();
    }

    /**
     * private volatile int[] serialNumber = {1};
     */
    private AtomicIntegerArray serialNumber = new AtomicIntegerArray(new int[]{1});

    /**
     * private volatile int[] serialNumber = {1};
     */
    private AtomicIntegerArray serialNumberSecond = new AtomicIntegerArray(new int[]{1});

    /**
     * 获取所有人当月考勤
     **/
    public List<ActualAttendanceVo> getActualAttendanceVos(int month, String workdays, String restWorkdays) throws ApiException {
        serialNumber.getAndSet(0, 1);
        List<String> workdayList = null;
        String monthString = String.valueOf(month);
        if (monthString.length() == 1) {
            monthString = "0" + monthString;
        }
        String finalMonthString = monthString;
        if (StringUtils.isNotEmpty(workdays)) {
            workdayList = Arrays.stream(workdays.split(",")).map(i -> {
                if (i.length() == 1) {
                    return "0" + i;
                }
                return i;
            }).map(i -> year + "-" + finalMonthString + "-" + i + " 00:00:00").collect(Collectors.toList());
        }
        List<String> restWorkdayList = null;
        if (StringUtils.isNotEmpty(restWorkdays)) {
            restWorkdayList = Arrays.stream(restWorkdays.split(",")).map(i -> {
                if (i.length() == 1) {
                    return "0" + i;
                }
                return i;
            }).map(i -> year + "-" + finalMonthString + "-" + i + " 00:00:00").collect(Collectors.toList());
        }
        String firstDayOfMonth = LocalDateUtil.getFirstDayOfMonth(year, month);
        String lastDayOfMonth = LocalDateUtil.getLastDayOfMonth(year, month);
        List<ActualAttendanceVo> attendanceVoList = new ArrayList<>();
        List<String> userIds = getUserIdList();
        Set<String> online = new HashSet<>(userIds);
        List<String> missIds = getMissingUserIdList();
        userIds.addAll(missIds);
        String accessToken = getAccessToken();
        Date start = LocalDateUtil.getScheduleTime(firstDayOfMonth);
        Date end = LocalDateUtil.getScheduleTime(lastDayOfMonth);
        List<Future> futures = new ArrayList<>();

        for (String userId : userIds) {
            CompletionService<ActualAttendanceVo> completionService =
                    new ExecutorCompletionService<>(LocalDateUtil.THREAD_POOL);
            List<String> finalWorkdayList = workdayList;
            List<String> restFinalWorkdayList = restWorkdayList;
            Future<ActualAttendanceVo> future = completionService.submit(() -> {
                log.info("当前用户-{}", userId);
                Date index = start;
                // 该月总加班分钟数
                long between = 0L;
                // 该月总加班分钟数
                long betweenAll = 0L;
                // 获取请假调休信息
                OapiAttendanceGetleavestatusResponse.LeaveStatusListVO leaveResult =
                        getLeaveInfo(accessToken, userId, start, end);
                List<OapiAttendanceGetleavestatusResponse.LeaveStatusVO> leaveStatus = leaveResult.getLeaveStatus();
                StringBuilder attendanceExceptionPeriod = new StringBuilder();
                for (int i = 0; i < 6; i++) {
                    if (index.after(end)) {
                        break;
                    }
                    // 获取7天内的打卡记录
                    List<OapiAttendanceListResponse.Recordresult> recordResult =
                            getRecordResult(accessToken, userId, index, end);
                    index = DateUtil.offset(index, DateField.DAY_OF_MONTH, 7);
                    if (CollectionUtil.isEmpty(recordResult)) {
                        continue;
                    }
                    log.info("在时间段{}-{}获取到打卡信息，共计-{}条", DateUtil.offset(index, DateField.DAY_OF_MONTH, -7).toString(),
                            DateUtil.offset(index, DateField.DAY_OF_MONTH, -1).toString(), recordResult.size());
                    // 打卡此时为奇数次时
                    if (recordResult.size() % 2 != 0) {
                        log.error("用户-{}在当前时间段-{} TO -{} 打卡异常", userId, index.toString(),
                                DateUtil.offset(index, DateField.DAY_OF_MONTH, -6).toDateStr());
                        attendanceExceptionPeriod.append(" ").append(DateUtil.offset(index,
                                DateField.DAY_OF_MONTH, -6).toDateStr()).append(" 00:00:00").append("-")
                                .append(index.toString());
                    }
                    for (int j = 0; j < recordResult.size(); ) {
                        if (j == recordResult.size() - 1) {
                            break;
                        }
                        OapiAttendanceListResponse.Recordresult re1;
                        OapiAttendanceListResponse.Recordresult re2;
                        if (recordResult.get(j).getCheckType().equals(ON_DUTY)) {
                            re1 = recordResult.get(j);
                            re2 = recordResult.get(j + 1);
                        } else {
                            re1 = recordResult.get(j + 1);
                            re2 = recordResult.get(j);
                        }
                        // 获取的两天不为同一天则跳过，也就是说当日打卡次数为奇数次时一律不计算加班时间
                        if (!DateUtils.isSameDay(re1.getBaseCheckTime(), re2.getBaseCheckTime())) {
                            j++;
                            log.info("当前j为-{}", j);
                            continue;
                        }
                        Date temporaryDate = re1.getWorkDate();
                        // 过滤出请假所在的工作日与当前工作日为同一天的工作日
                        List<OapiAttendanceGetleavestatusResponse.LeaveStatusVO> statusVOS = leaveStatus.stream()
                                .filter(leaveStatusVO -> DateUtils.isSameDay(temporaryDate,
                                        LocalDateUtil.getScheduleTimeByTimestamp(leaveStatusVO.getStartTime())))
                                .collect(Collectors.toList());
                        Date overtimeDate;
                        DateTime nineTimeDate = DateUtil.offsetMinute(DateUtil.beginOfDay(re1.getUserCheckTime()), 1110);
                        // 如果当前工作日没有请假且打卡时间在9点之后，则加班开始节点为员工打卡时间后的9.5小时
                        if (CollectionUtil.isEmpty(statusVOS)
                                && DateUtil.offsetMinute(DateUtil.beginOfDay(re1.getUserCheckTime()), 540)
                                .before(re1.getUserCheckTime())) {
                            overtimeDate = DateUtil.offsetMinute(re1.getUserCheckTime(), 570);
                        } else {// 2019-xx-xx 18:30:00 -->加班开始节点
                            overtimeDate = DateUtil.offsetMinute(DateUtil.beginOfDay(re1.getUserCheckTime()), 1110);
                        }
                        // 如果当前工作日是周末，且当前工作日不为法定工作日，则上班时间到下班时间为加班时间
                        if (LocalDateUtil.isWeekend(re1.getUserCheckTime())) {
                            AtomicBoolean notOvertimeDay = new AtomicBoolean(false);
                            if (!org.apache.commons.collections4.CollectionUtils.isEmpty(finalWorkdayList)) {
                                finalWorkdayList.forEach(workday -> {
                                    if (LocalDateUtil.getScheduleTime(workday)
                                            .equals(DateUtil.beginOfDay(re1.getUserCheckTime()))) {
                                        notOvertimeDay.set(true);
                                    }
                                });
                            }
                            // 周末不是工作日
                            if (!notOvertimeDay.get()) {
                                between += DateUtil.between(re1.getUserCheckTime(),
                                        re2.getUserCheckTime(), DateUnit.MINUTE);
                                betweenAll += DateUtil.between(re1.getUserCheckTime(), re2.getUserCheckTime(), DateUnit.MINUTE);
                                // 周末是工作日
                            } else {
                                between += DateUtil.between(overtimeDate, re2.getUserCheckTime(), DateUnit.MINUTE);
                                betweenAll += DateUtil.between(nineTimeDate, re2.getUserCheckTime(), DateUnit.MINUTE);
                            }
                            // 打卡时间超过加班节点则开始计算累计加班分钟数
                        } else if (re2.getUserCheckTime().after(overtimeDate)) {
                            AtomicBoolean notOvertimeDay = new AtomicBoolean(false);
                            if (!org.apache.commons.collections4.CollectionUtils.isEmpty(restFinalWorkdayList)) {
                                restFinalWorkdayList.forEach(workday -> {
                                    if (LocalDateUtil.getScheduleTime(workday)
                                            .equals(DateUtil.beginOfDay(re1.getUserCheckTime()))) {
                                        notOvertimeDay.set(true);
                                    }
                                });
                            }
                            if (notOvertimeDay.get()) {
                                String day = DateUtil.date(re1.getUserCheckTime()).toString("yyyy-MM-dd");
                                DateTime twelveOClock = DateUtil.parse(day + " 12:00:00");
                                DateTime nineOClock = DateUtil.parse(day + " 09:00:00");
                                Date startTime = DateUtil.date(re1.getUserCheckTime());
                                // 打开时间在九点中之前
                                if (nineOClock.after(re1.getUserCheckTime())) {
                                    startTime = nineOClock;
                                }
                                DateTime thirteenOClock = DateUtil.parse(day + " 12:00:00");
                                // 12 之前
                                if (re2.getUserCheckTime().before(twelveOClock)) {
                                    // 周一到周五是节假日
                                    between += DateUtil.between(startTime, twelveOClock, DateUnit.MINUTE);
                                } else if (re2.getUserCheckTime().before(thirteenOClock)) {
                                    between += DateUtil.between(startTime, twelveOClock, DateUnit.MINUTE);
                                } else {
                                    // 扣除中间一个小时
                                    between += DateUtil.between(startTime, re2.getUserCheckTime(), DateUnit.MINUTE) - 60;
                                }
                            } else {
                                between += DateUtil.between(overtimeDate, re2.getUserCheckTime(), DateUnit.MINUTE);
                                betweenAll += DateUtil.between(nineTimeDate, re2.getUserCheckTime(), DateUnit.MINUTE);
                            }

                        }
                        j += 2;
                    }
                    // 如果当前index时间大于本月最后一天则break
                    if (index.after(end)) {
                        break;
                    }
                }


                BigDecimal restTimes = getRestTimes(accessToken, userId, start, end, "调休");
                log.info("获取当前用户-{}当月调休时间-{}小时", userId, restTimes);

                BigDecimal overtimeHours =
                        BigDecimal.valueOf(between).divide(new BigDecimal(60), 2, RoundingMode.HALF_UP);

                BigDecimal allHours =
                        BigDecimal.valueOf(betweenAll).divide(new BigDecimal(60), 2, RoundingMode.HALF_UP);
                log.info("处理完加班时间，开始组装VO，加班时间为-{}", overtimeHours);
                BigDecimal decimal = overtimeHours;
                overtimeHours = overtimeHours.subtract(restTimes);
                List<OapiSmartworkHrmEmployeeListResponse.EmpFieldInfoVO> usernameByUserId =
                        getUsernameByUserId(accessToken, userId);
                ActualAttendanceVo attendanceVo =
                        build(serialNumber, usernameByUserId.get(0).getFieldList().get(0).getValue(),
                                getLeaveHours(leaveResult), overtimeHours, firstDayOfMonth, lastDayOfMonth);
                attendanceVo.setAttendanceExceptionPeriod(attendanceExceptionPeriod.toString());
                attendanceVo.setActual(allHours);
                attendanceVo.setActualTime(decimal);
                attendanceVo.setRestTime(decimal.subtract(attendanceVo.getActualOvertime()));
                if (online.contains(userId)) {
                    attendanceVo.setStatus("在职");
                    List<String> list = Arrays.asList("病假", "产假", "婚假", "例假", "哺乳假", "陪产假", "事假", "丧假", "年假");
                    for (String s : list) {
                        switch (s) {
                            case "病假":
                                attendanceVo.setSickLeave(getRestTimes(accessToken, userId, start, end, s));
                                break;
                            case "产假":
                                attendanceVo.setMaternityLeave(getRestTimes(accessToken, userId, start, end, s));
                                break;
                            case "婚假":
                                attendanceVo.setWeddingLeave(getRestTimes(accessToken, userId, start, end, s));
                                break;
                            case "例假":
                                attendanceVo.setOfficialHoliday(getRestTimes(accessToken, userId, start, end, s));
                                break;
                            case "哺乳假":
                                attendanceVo.setBreastfeedingLeave(getRestTimes(accessToken, userId, start, end, s));
                                break;
                            case "事假":
                                attendanceVo.setLeave(getRestTimes(accessToken, userId, start, end, s));
                                break;
                            case "年假":
                                attendanceVo.setAnnual(getRestTimes(accessToken, userId, start, end, s));
                                break;
                            case "丧假":
                                attendanceVo.setFuneralLeave(getRestTimes(accessToken, userId, start, end, s));
                                break;
                        }
                    }

                }
                attendanceVoList.add(attendanceVo);
                serialNumber.getAndIncrement(0);
                return attendanceVo;
            });
            futures.add(future);
        }

        futures.forEach(future -> {
            try {
                future.get();
                log.info("-----------------------------------get-------------------------------");
            } catch (Exception e) {
                log.error("-----------------------------------get error-------------------------------");
                e.printStackTrace();
            }
        });
        return attendanceVoList;
    }

    public void test() throws ApiException {
        List<String> userIds = getUserIdList();
        String accessToken = getAccessToken();
        for (String userId : userIds) {
            List<OapiSmartworkHrmEmployeeListResponse.EmpFieldInfoVO> usernameByUserId = getUsernameByUserId(accessToken, userId);
            System.out.println(usernameByUserId.get(0).getFieldList().get(0).getValue());
        }
    }

    public List<String> getUserName() throws ApiException {
        List<String> userIds = getUserIdList();
        String accessToken = getAccessToken();
        List<String> list = new ArrayList<>();
        for (String userId : userIds) {
            List<OapiSmartworkHrmEmployeeListResponse.EmpFieldInfoVO> usernameByUserId = getUsernameByUserId(accessToken, userId);
            list.add(usernameByUserId.get(0).getFieldList().get(0).getValue());
        }
        return list;
    }

    public List<OapiUserGetResponse> getUserDepotInfo() throws ApiException {
        List<String> userIds = getUserIdList();
        /*      userIds.addAll(getMissingUserIdList());*/
        String accessToken = getAccessToken();
        List<OapiUserGetResponse> objects = new ArrayList<>();
        userIds.forEach(i -> objects.add(getUserDepotInfo(accessToken, i)));
        return objects;
    }

    public List<String> getUserDepotAndUserIdAndUserName() throws ApiException {
        List<OapiUserGetResponse> userDepotInfo = getUserDepotInfo();
        return userDepotInfo.stream().map(i -> CollectionUtil.isEmpty(i.getDepartment()) ? "无部门" : i.getDepartment().get(0) + "_" + i.getUserid() + "_" + i.getName()).collect(Collectors.toList());
    }

    private String getUserInfoByUserId(String userId) throws ApiException {
        DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/user/get");
        OapiUserGetRequest req = new OapiUserGetRequest();
        req.setUserid(userId);
        req.setHttpMethod("GET");
        OapiUserGetResponse rsp = client.execute(req, getAccessToken());
        return "离职无部门_" + req.getUserid() + "_" + rsp.getName();
    }

    public List<String> getUserDepotAndUserIdAndUserNameAllUser() throws ApiException {
        List<String> userDepotAndUserIdAndUserName = getUserDepotAndUserIdAndUserName();
        List<String> objects = new ArrayList<>();
        getMissingUserIdList().forEach(i -> {
            try {
                objects.add(getUserInfoByUserId(i));
            } catch (ApiException e) {
                log.error("当前用户出错:{}", i);
            }
        });
        userDepotAndUserIdAndUserName.addAll(objects);
        return userDepotAndUserIdAndUserName;
    }

    /**
     * 获取所有人当月考勤
     **/
    public List<YearActualAttendanceVO> getYearActualAttendanceVos() throws ApiException {
        serialNumberSecond.getAndSet(0, 1);
        List<YearActualAttendanceVO> attendanceVoList = new ArrayList<>();
        List<String> userIds = getUserIdList();
        String accessToken = getAccessToken();
        List<Future> futures = new ArrayList<>();
        for (String userId : userIds) {
            CompletionService<YearActualAttendanceVO> completionService =
                    new ExecutorCompletionService<>(LocalDateUtil.THREAD_POOL);
            Future<YearActualAttendanceVO> future = completionService.submit(() -> {
                YearActualAttendanceVO attendanceVo = new YearActualAttendanceVO();
                for (int z = 8; z < 13; z++) {
                    String firstDayOfMonth = LocalDateUtil.getFirstDayOfMonth(year, z);
                    String lastDayOfMonth = LocalDateUtil.getLastDayOfMonth(year, z);
                    Date start = LocalDateUtil.getScheduleTime(firstDayOfMonth);
                    Date end = LocalDateUtil.getScheduleTime(lastDayOfMonth);
                    log.info("当前用户-{}当前月-{}", userId, z);
                    Date index = start;
                    // 该月总加班分钟数
                    long between = 0L;
                    // 获取请假调休信息
                    OapiAttendanceGetleavestatusResponse.LeaveStatusListVO leaveResult =
                            getLeaveInfo(accessToken, userId, start, end);
                    List<OapiAttendanceGetleavestatusResponse.LeaveStatusVO> leaveStatus = leaveResult.getLeaveStatus();
                    for (int i = 0; i < 6; i++) {
                        if (index.after(end)) {
                            break;
                        }
                        // 获取7天内的打卡记录
                        List<OapiAttendanceListResponse.Recordresult> recordResult =
                                getRecordResult(accessToken, userId, index, end);
                        index = DateUtil.offset(index, DateField.DAY_OF_MONTH, 7);
                        if (CollectionUtil.isEmpty(recordResult)) {
                            continue;
                        }
                        log.info("在时间段{}-{}获取到打卡信息，共计-{}条", DateUtil.offset(index, DateField.DAY_OF_MONTH, -7).toString(),
                                DateUtil.offset(index, DateField.DAY_OF_MONTH, -1).toString(), recordResult.size());
                        // 打卡此时为奇数次时
                        if (recordResult.size() % 2 != 0) {
                            log.error("用户-{}在当前时间段-{} TO -{} 打卡异常", userId, index.toString(),
                                    DateUtil.offset(index, DateField.DAY_OF_MONTH, -6).toDateStr());
                        }
                        for (int j = 0; j < recordResult.size(); ) {
                            if (j == recordResult.size() - 1) {
                                break;
                            }
                            OapiAttendanceListResponse.Recordresult re1;
                            OapiAttendanceListResponse.Recordresult re2;
                            if (recordResult.get(j).getCheckType().equals(ON_DUTY)) {
                                re1 = recordResult.get(j);
                                re2 = recordResult.get(j + 1);
                            } else {
                                re1 = recordResult.get(j + 1);
                                re2 = recordResult.get(j);
                            }
                            // 获取的两天不为同一天则跳过，也就是说当日打卡次数为奇数次时一律不计算加班时间
                            if (!DateUtils.isSameDay(re1.getBaseCheckTime(), re2.getBaseCheckTime())) {
                                j++;
                                log.info("当前j为-{}", j);
                                continue;
                            }
                            Date temporaryDate = re1.getWorkDate();
                            // 过滤出请假所在的工作日与当前工作日为同一天的工作日
                            List<OapiAttendanceGetleavestatusResponse.LeaveStatusVO> statusVOS = leaveStatus.stream()
                                    .filter(leaveStatusVO -> DateUtils.isSameDay(temporaryDate,
                                            LocalDateUtil.getScheduleTimeByTimestamp(leaveStatusVO.getStartTime())))
                                    .collect(Collectors.toList());
                            Date overtimeDate;
                            // 如果当前工作日没有请假且打卡时间在9点之后，则加班开始节点为员工打卡时间后的9.5小时
                            if (CollectionUtil.isEmpty(statusVOS)
                                    && DateUtil.offsetMinute(DateUtil.beginOfDay(re1.getUserCheckTime()), 540)
                                    .before(re1.getUserCheckTime())) {
                                overtimeDate = DateUtil.offsetMinute(re1.getUserCheckTime(), 570);
                            } else {// 2019-xx-xx 18:30:00 -->加班开始节点
                                overtimeDate = DateUtil.offsetMinute(DateUtil.beginOfDay(re1.getUserCheckTime()), 1110);
                            }
                            between += DateUtil.between(overtimeDate, re2.getUserCheckTime(), DateUnit.MINUTE);
                            j += 2;
                        }
                        // 如果当前index时间大于本月最后一天则break
                        if (index.after(end)) {
                            break;
                        }
                    }
                    BigDecimal restTimes = getRestTimes(accessToken, userId, start, end, "调休");
                    log.info("获取当前用户-{}当月-{}调休时间-{}小时", userId, z, restTimes);
                    BigDecimal overtimeHours =
                            BigDecimal.valueOf(between).divide(new BigDecimal(60), 2, RoundingMode.HALF_UP);
                    log.info("处理完加班时间，开始组装VO，加班时间为-{}", overtimeHours);
                    overtimeHours = overtimeHours.subtract(restTimes);
                    switch (z) {
                        case 1:
                            attendanceVo.setJanuary(overtimeHours);
                            attendanceVo.setJanuaryRest(restTimes);
                            break;
                        case 2:
                            attendanceVo.setFebruary(overtimeHours);
                            attendanceVo.setFebruaryRest(restTimes);
                            break;
                        case 3:
                            attendanceVo.setMarch(overtimeHours);
                            attendanceVo.setMarchRest(restTimes);
                            break;
                        case 4:
                            attendanceVo.setApril(overtimeHours);
                            attendanceVo.setAprilRest(restTimes);
                            break;
                        case 5:
                            attendanceVo.setMay(overtimeHours);
                            attendanceVo.setMayRest(restTimes);
                            break;
                        case 6:
                            attendanceVo.setJune(overtimeHours);
                            attendanceVo.setJuneRest(restTimes);
                            break;
                        case 7:
                            attendanceVo.setJuly(overtimeHours);
                            attendanceVo.setJulyRest(restTimes);
                            break;
                        case 8:
                            attendanceVo.setAugust(overtimeHours);
                            attendanceVo.setAugustRest(restTimes);
                            break;
                        case 9:
                            attendanceVo.setSeptember(overtimeHours);
                            attendanceVo.setSeptemberRest(restTimes);
                            break;
                        case 10:
                            attendanceVo.setOctober(overtimeHours);
                            attendanceVo.setOctoberRest(restTimes);
                            break;
                        case 11:
                            attendanceVo.setNovember(overtimeHours);
                            attendanceVo.setNovemberRest(restTimes);
                            break;
                        case 12:
                            attendanceVo.setDecember(overtimeHours);
                            attendanceVo.setDecemberRest(restTimes);
                            break;
                        default:
                    }
                }
                List<OapiSmartworkHrmEmployeeListResponse.EmpFieldInfoVO> usernameByUserId =
                        getUsernameByUserId(accessToken, userId);
                attendanceVo.setUserName(usernameByUserId.get(0).getFieldList().get(0).getValue());
                attendanceVoList.add(attendanceVo);
                serialNumberSecond.getAndIncrement(0);
                return attendanceVo;
            });
            futures.add(future);
        }
        futures.forEach(future -> {
            try {
                future.get();
                log.info("-----------------------------------get-------------------------------");
            } catch (Exception e) {
                log.error("-----------------------------------get error-------------------------------");
            }
        });
        return attendanceVoList;
    }

    private BigDecimal getRestTimes(String accessToken, String userId, Date from, Date to, String rest) {
        DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/attendance/getleavetimebynames");
        OapiAttendanceGetleavetimebynamesRequest req = new OapiAttendanceGetleavetimebynamesRequest();
        req.setUserid(userId);
        req.setLeaveNames(rest);
        req.setFromDate(from);
        req.setToDate(to);
        OapiAttendanceGetleavetimebynamesResponse rsp;
        try {
            rsp = client.execute(req, accessToken);
        } catch (ApiException e) {
            log.error("根据当前用户-{}请求获取调休时间接口失败", userId, e);
            throw new ServiceException(ServiceCodeEnum.FAIL.getCode(), "请求获取调休时间接口失败");
        }
        if (rsp == null) {
            log.error("根据当前用户-{}请求获取调休时间为空", userId);
            return new BigDecimal(0);
        }
        List<OapiAttendanceGetleavetimebynamesResponse.ColumnDayAndVal> list = rsp.getResult().getColumns().get(0).getColumnvals();
        BigDecimal turnRest = new BigDecimal(0);
        for (OapiAttendanceGetleavetimebynamesResponse.ColumnDayAndVal c : list) {
            turnRest = turnRest.add(new BigDecimal(c.getValue()));
        }
        return turnRest;
    }

    /**
     * 获取userList
     **/
    public List<String> getUserIdList() throws ApiException {
        String accessToken = getAccessToken();
        DingTalkClient client =
                new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/smartwork/hrm/employee/queryonjob");
        List<String> userIds = new ArrayList<>();
        Long offset = 0L;
        do {
            OapiSmartworkHrmEmployeeQueryonjobRequest req = new OapiSmartworkHrmEmployeeQueryonjobRequest();
            req.setStatusList("2,3,5,-1");
            req.setOffset(offset);
            req.setSize(20L);
            OapiSmartworkHrmEmployeeQueryonjobResponse response = client.execute(req, accessToken);
            List<String> pageUserIds = response.getResult().getDataList();
            if (!CollectionUtil.isEmpty(pageUserIds)) {
                // 添加每次分页获取的userId，单次只能获取20个id;
                userIds.addAll(pageUserIds);
            }
            offset = response.getResult().getNextCursor();
        } while (offset != null);
        return userIds;
    }

    private List<String> getMissingUserIdList() throws ApiException {
        String accessToken = getAccessToken();
        DingTalkClient client =
                new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/smartwork/hrm/employee/querydimission");
        List<String> userIds = new ArrayList<>();
        Long offset = 0L;
        do {
            OapiSmartworkHrmEmployeeQueryonjobRequest req = new OapiSmartworkHrmEmployeeQueryonjobRequest();
            req.setStatusList("0");
            req.setOffset(offset);
            req.setSize(20L);
            OapiSmartworkHrmEmployeeQueryonjobResponse response = client.execute(req, accessToken);
            List<String> pageUserIds = response.getResult().getDataList();
            if (!CollectionUtil.isEmpty(pageUserIds)) {
                // 添加每次分页获取的userId，单次只能获取20个id;
                userIds.addAll(pageUserIds);
            }
            offset = response.getResult().getNextCursor();
        } while (offset != null);
        return userIds;
    }

    /**
     * 获取当前用户当前月的请假信息
     **/
    private OapiAttendanceGetleavestatusResponse.LeaveStatusListVO getLeaveInfo(String accessToken, String userId,
                                                                                Date start, Date end) {
        DingTalkClient leaveClient =
                new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/attendance/getleavestatus");
        OapiAttendanceGetleavestatusRequest req = new OapiAttendanceGetleavestatusRequest();
        req.setUseridList(userId);
        req.setStartTime(start.getTime());
        req.setEndTime(end.getTime());
        req.setOffset(0L);
        req.setSize(20L);
        OapiAttendanceGetleavestatusResponse rsp;
        try {
            rsp = leaveClient.execute(req, accessToken);
        } catch (ApiException e) {
            log.error("根据当前用户-{}请求获取请假信息接口失败~", userId, e);
            throw new ServiceException(ServiceCodeEnum.FAIL.getCode(), "请求获取请假信息接口失败~");
        }
        return rsp.getResult();
    }

    /**
     * 获取7天内的打卡记录
     **/
    private List<OapiAttendanceListResponse.Recordresult> getRecordResult(String accessToken, String userId, Date index,
                                                                          Date end) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        DingTalkClient attendanceClient = new DefaultDingTalkClient("https://oapi.dingtalk.com/attendance/list");
        OapiAttendanceListRequest request = new OapiAttendanceListRequest();
        request.setWorkDateFrom(simpleDateFormat.format(index));
        index = DateUtil.offset(index, DateField.DAY_OF_MONTH, 6);
        if (index.after(end)) {
            index = end;
        }
        request.setWorkDateTo(simpleDateFormat.format(index));
        request.setUserIdList(Collections.singletonList(userId));
        request.setOffset(0L);
        request.setLimit(50L);
        OapiAttendanceListResponse response;
        try {
            response = attendanceClient.execute(request, accessToken);
        } catch (ApiException e) {
            log.error("用户-{}获取当前时间段-{} TO -{} 打卡记录异常~", userId, index.toString(),
                    DateUtil.offset(index, DateField.DAY_OF_MONTH, -6).toDateStr(), e);
            throw new ServiceException(ServiceCodeEnum.FAIL.getCode(), "请求获取打卡记录接口失败~");
        }
        return response.getRecordresult();
    }

    /**
     * 根据用户ID获取用户名
     **/
    private List<OapiSmartworkHrmEmployeeListResponse.EmpFieldInfoVO> getUsernameByUserId(String accessToken,
                                                                                          String userId) {
        DingTalkClient nameClient =
                new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/smartwork/hrm/employee/list");
        OapiSmartworkHrmEmployeeListRequest request = new OapiSmartworkHrmEmployeeListRequest();
        request.setUseridList(userId);
        request.setFieldFilterList(FIELD);
        OapiSmartworkHrmEmployeeListResponse nameResponse;
        try {
            nameResponse = nameClient.execute(request, accessToken);
        } catch (ApiException e) {
            log.error("当前用户-{}获取用户名失败~", userId, e);
            throw new ServiceException(ServiceCodeEnum.FAIL.getCode(), "请求获取用户名接口失败~");
        }
        return nameResponse.getResult();
    }

    private OapiUserGetResponse getUserDepotInfo(String accessToken, String userId) {
        DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/user/get");
        OapiUserGetRequest req = new OapiUserGetRequest();
        req.setUserid(userId);
        req.setHttpMethod("GET");
        OapiUserGetResponse rsp = null;
        try {
            rsp = client.execute(req, accessToken);
        } catch (ApiException e) {
            e.printStackTrace();
        }
        return rsp;
    }


    /**
     * 获取已审批的请假小时数
     **/
    private BigDecimal getLeaveHours(OapiAttendanceGetleavestatusResponse.LeaveStatusListVO leaveResult) {
        List<OapiAttendanceGetleavestatusResponse.LeaveStatusVO> leaveStatus = leaveResult.getLeaveStatus();
        BigDecimal totalHours = new BigDecimal(0);
        if (CollectionUtil.isEmpty(leaveStatus)) {
            return totalHours;
        }
        for (OapiAttendanceGetleavestatusResponse.LeaveStatusVO leaveStatusVO : leaveStatus) {
            if (leaveStatusVO.getDurationUnit().equals("percent_day")) {
                totalHours = totalHours.add(BigDecimal.valueOf(leaveStatusVO.getDurationPercent())
                        .divide(new BigDecimal(100), 2, RoundingMode.HALF_UP).multiply(new BigDecimal(8)));
            } else if (leaveStatusVO.getDurationUnit().equals("percent_hour")) {
                totalHours = totalHours.add(BigDecimal.valueOf(leaveStatusVO.getDurationPercent())
                        .divide(new BigDecimal(100), 2, RoundingMode.HALF_UP));
            }
        }
        return totalHours;
    }

    /**
     * 组装actualAttendanceVo
     **/
    private ActualAttendanceVo build(AtomicIntegerArray serialNumber, String userName, BigDecimal leaveHours, BigDecimal overtimeHours,
                                     String beginOfMonth, String endOfMonth) {
        return ActualAttendanceVo.builder().serialNumber(serialNumber.get(0))
                .userName(userName).startDate(beginOfMonth.substring(0, 10)).endDate(endOfMonth.substring(0, 10))
                .standardHours(new BigDecimal(8)).leaveHours(leaveHours).actualOvertime(overtimeHours)
                .standardDays(STANDARD_DAYS).validDays(null).accountDate(beginOfMonth.substring(0, 7)).reckoner(RECKONER)
                .employeeState(1).status("离职").build();
    }
}
